import { intToBytes, bytesToInt } from "./bytesTool";

var _ws = null;
var _url = null;
var _lastHeartBeat = 0;
var _heartBeatTime = 30;
var _dataBuffer = new ArrayBuffer(0);
var _dataArray = [];
var _isReading = false;
var _openCallback = null;
var _messageCallback = null;
var _errorCallback = null;

var _reconnecting = false;

var WebSocketEnum = {};
WebSocketEnum.rsaPublicKey = 0x01;
WebSocketEnum.aesDecodeKey = 0x02;
WebSocketEnum.heartbeatSend = 0x03;
WebSocketEnum.heartbeatReceived = 0x04;
WebSocketEnum.normalMessage = 0x10;

function initWebSocket() {
  _url = localStorage.getItem("ws");
  if (!_url || _url == undefined || _url == "undefined" || _url.length <= 0) return;
  console.log("WS url=", _url);
  _ws = new WebSocket(_url, ["binary"]);
  _ws.binaryType = "arraybuffer";
  _ws.onmessage = onMessageCallback;
  _ws.onopen = onOpenCallback;
  _ws.onerror = onErrorCallback;
  _ws.onclose = onCloseCallback;
}

function onOpenCallback() {
  console.log("WS was opened.");

  _dataBuffer = new ArrayBuffer(0);
  _dataArray = [];

  timer();

  if (_openCallback) {
    _openCallback();
  }
}

function onErrorCallback(event) {
  console.log("WS error", event);
  // if (_errorCallback) {
  //   _errorCallback(event);
  // }
  // 连接建立失败重连
  reconnect();
}

function onMessageCallback(event) {
  // console.log("WS received message ", event);
  // 数据接收
  // const redata = JSON.parse(e.data);
  //   if (_messageCallback) {
  //     _messageCallback(e.data);
  //   }

  if (event.data != null && event.data.byteLength > 0) {
    receivedBuffers(event.data);
  }
}

function onCloseCallback(event) {
  // 关闭
  console.log("WS closed", event);
  reconnect();
}

function send(Data) {
  // console.log("WS send ", Data);
  // 数据发送
  _ws.send(Data);
}

function setCallback(openCallback, messageCallback, errorCallback) {
  _openCallback = openCallback;
  _messageCallback = messageCallback;
  _errorCallback = errorCallback;
}

function sendProtoData(data) {
  if (!_ws) {
    // if (_errorCallback) {
    //   _errorCallback("ws not ready");
    // }
    console.log("WS not ready");
    return;
  }

  if (_ws && _ws.readyState === WebSocket.OPEN) {
    // 若是ws开启状态
    send(packageData(WebSocketEnum.normalMessage, data));
  } else if (_ws && _ws.readyState === WebSocket.CONNECTING) {
    // 若是 正在开启状态，则等待1s后重新调用
    setTimeout(function() {
      send(packageData(WebSocketEnum.normalMessage, data));
    }, 1000);
  } else {
    // 若未开启 ，则等待1s后重新调用
    setTimeout(function() {
      sendProtoData(data);
    }, 1000);
  }
}

function packageData(dataType, protoArray) {
  var protoArrayLength = protoArray.byteLength;

  var result = new ArrayBuffer(protoArrayLength + 5);
  // 长度
  var length = new Uint8Array(result, 0, 4);
  length.set(intToBytes(protoArray.byteLength));
  // 类型
  var type = new Uint8Array(result, 4, 1);
  type[0] = dataType;

  if (result.byteLength > 5) {
    // protobuf数据
    var data = new Uint8Array(result, 5, protoArray.byteLength);
    data.set(protoArray);
  }

  return result;
}

async function receivedBuffers(buffer) {
  if (_isReading) {
    _dataArray.push(buffer);
    return;
  }

  _isReading = true;
  // console.log(buffer);
  var newBuffer = combineBuffer(buffer);
  var lengthArray = new Uint8Array(newBuffer, 0, 4);
  var type = new Uint8Array(newBuffer, 4, 1);

  while (true) {
    var length = bytesToInt(lengthArray);
    var newLength = newBuffer.byteLength - (length + 5);
    // console.log(length);
    // console.log(newLength);
    // console.log(type[0]);
    if (length >= 0 && newLength >= 0) {
      if (newLength > 0) {
        console.log("WS length = " + length + " remain Length = " + newLength);
      }

      var data = new Uint8Array(newBuffer, 5, length);
      completeBuffer(type[0], data);

      _dataBuffer = new ArrayBuffer(newLength);
      if (newLength > 0) {
        var cache = new Uint8Array(_dataBuffer, 0, newLength);
        cache.set(new Uint8Array(newBuffer, length + 5, newLength));
      }

      if (newLength <= 5) {
        break;
      } else {
        newBuffer = _dataBuffer;
        lengthArray = new Uint8Array(newBuffer, 0, 4);
        type = new Uint8Array(newBuffer, 4, 1);
      }
    } else {
      console.log(
        "WS data need combine. target length = " +
          length +
          ", but now length = " +
          (length + newLength)
      );
      _dataBuffer = newBuffer;
      break;
    }
  }

  newBuffer = new ArrayBuffer(0);

  _isReading = false;
  if (_dataArray.length > 0) {
    receivedBuffers(_dataArray[0]);
    _dataArray.shift();
  }
}

function combineBuffer(buffer) {
  var result = new ArrayBuffer(_dataBuffer.byteLength + buffer.byteLength);
  var cache = new Uint8Array(result, 0, _dataBuffer.byteLength);
  var add = new Uint8Array(result, 0, buffer.byteLength);
  cache.set(new Uint8Array(_dataBuffer, 0, _dataBuffer.byteLength));
  add.set(new Uint8Array(buffer, 0, buffer.byteLength));

  return result;
}

function completeBuffer(type, data) {
  switch (type) {
    case WebSocketEnum.rsaPublicKey:
    case WebSocketEnum.aesDecodeKey: {
      break;
    }

    case WebSocketEnum.heartbeatSend: {
      heartbeat(true);
      break;
    }

    case WebSocketEnum.heartbeatReceived: {
      break;
    }

    case WebSocketEnum.normalMessage: {
      // console.log("WS data length = " + data.byteLength);
      callback(data);
      break;
    }
    default: {
      break;
    }
  }
}

function callback(bufferAck) {
  // if(this._sleep) {
  //     console.log("!!! 过滤数据了！ ");
  //     return;
  // }

  // 回调出去
  if (_messageCallback) {
    _messageCallback(bufferAck);
  }
}

async function reconnect() {
  if (_reconnecting) return;

  _reconnecting = true;

  setTimeout(() => {
    _reconnecting = false;
    if (_ws && _ws.readyState === WebSocket.OPEN) {
      //
    } else {
      initWebSocket();
    }
  }, 5000);
}

async function timer() {
  setTimeout(() => {
    if (Date.now() - _lastHeartBeat >= _heartBeatTime * 1000) {
      heartbeat();
    }

    if (_ws && _ws.readyState === WebSocket.OPEN) {
      timer();
    }
  }, _heartBeatTime * 1000);
}

function heartbeat(received = false) {
  if (received) {
    console.log("WS send heartbeatAck.");
    send(packageData(WebSocketEnum.heartbeatReceived, new Uint8Array(0)));
  } else {
    console.log("WS send heartbeat.");
    send(packageData(WebSocketEnum.heartbeatSend, new Uint8Array(0)));
  }
}

function close() {
  if (_ws) {
    _ws.close();
  }
}

try {
  initWebSocket();
} catch (error) {
  if (_errorCallback) {
    _errorCallback(error);
  }
}

// 将方法暴露出去
export { initWebSocket, sendProtoData, setCallback, close, callback };
